/*
 * Copyright 2024 The CHANGLEI Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

part of '../px.dart';

/// 像素大小
extension OffsetToPx on Offset {
  /// 像素大小
  PxOffset get px => PxOffset(dx.px, dy.px);

  /// 像素大小
  PxOffset toPx(BuildContext context) {
    return PxOffset(
      dx.toPx(context),
      dy.toPx(context),
    );
  }
}

/// Created by changlei on 2024/4/23.
///
/// See [Offset]
class PxOffset extends PxOffsetBase implements _Px<Offset> {
  /// See [Offset.new]
  const PxOffset(super.dx, super.dy);

  /// See [Offset.fromDirection]
  factory PxOffset.fromDirection(PxDouble direction, [PxDouble distance = const PxDouble(1)]) {
    return PxOffset(distance * cos(direction), distance * sin(direction));
  }

  /// See [Offset.dx]
  PxDouble get dx => _dx;

  /// See [Offset.dy]
  PxDouble get dy => _dy;

  /// See [Offset.distance]
  PxDouble get distance => sqrt(distanceSquared);

  /// See [Offset.distanceSquared]
  PxDouble get distanceSquared => dx * dx + dy * dy;

  /// See [Offset.direction]
  PxDouble get direction => atan2(dy, dx);

  /// See [Offset.zero]
  static const PxOffset zero = PxOffset(PxDouble.zero, PxDouble.zero);

  /// See [Offset.infinity]
  static const PxOffset infinite = PxOffset(PxDouble.infinity, PxDouble.infinity);

  /// See [Offset.scale]
  PxOffset scale(double scaleX, double scaleY) => PxOffset(dx * scaleX.px, dy * scaleY.px);

  /// See [Offset.translate]
  PxOffset translate(PxDouble translateX, PxDouble translateY) => PxOffset(dx + translateX, dy + translateY);

  /// See [Offset.-]
  PxOffset operator -() => PxOffset(-dx, -dy);

  /// See [Offset.-]
  PxOffset operator -(PxOffset other) => PxOffset(dx - other.dx, dy - other.dy);

  /// See [Offset.+]
  PxOffset operator +(PxOffset other) => PxOffset(dx + other.dx, dy + other.dy);

  /// See [Offset.*]
  PxOffset operator *(PxDouble operand) => PxOffset(dx * operand, dy * operand);

  /// See [Offset./]
  PxOffset operator /(PxDouble operand) => PxOffset(dx / operand, dy / operand);

  /// See [Offset.~/]
  PxOffset operator ~/(PxDouble operand) => PxOffset((dx ~/ operand).toDouble(), (dy ~/ operand).toDouble());

  /// See [Offset.%]
  PxOffset operator %(PxDouble operand) => PxOffset(dx % operand, dy % operand);

  /// See [Offset.&]
  PxRect operator &(PxSize other) => PxRect.fromLTWH(dx, dy, other.width, other.height);

  /// See [Offset.lerp]
  static PxOffset? lerp(PxOffset? a, PxOffset? b, double t) {
    if (b == null) {
      if (a == null) {
        return null;
      } else {
        return a * (1.0 - t).px;
      }
    } else {
      if (a == null) {
        return b * t.px;
      } else {
        return PxOffset(_lerpPxDouble(a.dx, b.dx, t), _lerpPxDouble(a.dy, b.dy, t));
      }
    }
  }

  @override
  Offset toDp(BuildContext context) {
    return Offset(
      dx.toDp(context),
      dy.toDp(context),
    );
  }

  @override
  bool operator ==(Object other) {
    return other is PxOffset && other.dx == dx && other.dy == dy;
  }

  @override
  int get hashCode => Object.hash(dx, dy);

  @override
  String toString() => 'PxOffset(${dx.toStringAsFixed(1)}, ${dy.toStringAsFixed(1)})';
}
